using System;
using System.Drawing;
using System.Drawing.Imaging;

namespace ru.yourpictures.Core.Imaging.Filters.PhotoFilters
{
    /// <summary>
    /// This fisheye filter code (by Paul Kinlan) was taken partly from the following site (with permission of course)
    /// http://www.kinlan.co.uk/2005/05/fisheye-example-code-that-integrates.html
    /// Reference: http://www.codeproject.com/KB/cs/yael_image_filters.aspx
    /// </summary>
    [FilterImplementation(GdiPlusImplemented = false, LowLevelImplemented = true)]
    public class FishEye : FilterBase
    {
        #region TODOs
        // Add parameter Radius (the radius of the magic eye)
        // Add parameter Center of magic eye
        #endregion

        private float _curvature = 0.1f;

        #region Public properties
        /// <summary>
        /// Determins the FishEye effect intensity
        /// </summary>
        public float Curvature
        {
            get { return _curvature; }
            set { _curvature = value; }
        }
        #endregion

        #region Ctor
        public FishEye(float curvature)
        {
            _curvature = curvature;
        }
        public FishEye() { }
        #endregion

        #region ValidateArgs
        private void ValidateArgs()
        {
            if (_curvature <= 0.0f)
                throw new ArgumentException("Curvature must be set and it must be > 0.0f");
        }
        #endregion

        #region FilterImplementation
        /// <summary>
        /// Executes this filter on the input image and returns the result
        /// </summary>
        /// <param name="inputImage">input image</param>
        /// <returns>Creates a FishEye effect</returns>
        /// <example>
        /// <code>
        /// Image transformed;
        /// FisheyeFilter fishI = new FisheyeFilter();
        /// fishI.Curvature = 0.15f
        /// transformed = fishI.ExecuteFilter(myImg);
        /// </code>
        /// </example>
        protected override void FilterImplementation()
        {
            ValidateArgs();

            // get source image size
            int halfWidth = _width / 2;
            int halfHeight = _height / 2;

            //Obtain the Maxium Size Of the Fish Eye
            int maxRadius = (int)Math.Min(_width, _height) / 2;

            //The S parameter mentioned in Jason Walton Algo.
            double s = maxRadius / Math.Log(_curvature * maxRadius + 1);

            // This array will be used as readonly reference (original image)
            byte[] src = CopyArray(_pixels);

            int index = 0;
            int newIndex = 0;

            for (int y = -1 * halfHeight; y < _height - halfHeight; y++)
            {
                for (int x = -1 * halfWidth; x < _width - halfWidth; x++)
                {
                    //Get the Current Pixels Polar Co-ordinates
                    double radius = Math.Sqrt(x * x + y * y);
                    double angle = Math.Atan2(y, x);

                    //Check to see if the polar pixel is out of bounds
                    if (radius <= maxRadius)
                    {
                        //Current Pixel is inside the Fish Eye
                        //newRad is the pixel that we want to shift to current pixel based on the fish eye posistion
                        double newRad = (Math.Exp(radius / s) - 1) / _curvature;
                        int newX = (int)(newRad * Math.Cos(angle));
                        int newY = (int)(newRad * Math.Sin(angle));

                        newX += halfWidth;
                        newY += halfHeight;

                        //Chage the Pointer to the Src
                        newIndex = (y + halfHeight) * _stride + (x + halfWidth) * _bytesPerPixel;
                        index = newY * _stride + newX * _bytesPerPixel;

                        //Change the Pointer to the destination
                        CopyPixel(newIndex, src, index, _pixels, _bytesPerPixel);
                    }
                    else
                    {
                        //Current Pixel is outside the Fish Eye thus should be output as is
                        //_pixels[y * _stride + x] = src[y * _stride + x];
                        index = (y + halfHeight) * _stride + (x + halfWidth) * _bytesPerPixel;
                        CopyPixel(index, src, index, _pixels, _bytesPerPixel);
                    }
                }
            }
        }
        #endregion

        #region Public Filter Methods
        public override void Apply(Bitmap b)
        {
            b = Convert(b);
            _b = b;
        }

        public override Bitmap Convert(Bitmap b)
        {
            throw new NotImplementedException("GDI+ mode is not implemented");
        }
        #endregion
    }
}
